﻿﻿//mytetris.cpp: 定义应用程序的入口点。
 //

#include "stdafx.h"
#include "mytetris.h"

#include <gtest\gtest.h>

#define MAX_LOADSTRING 100

 // 全局变量: 
	HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
HMENU StartMenu;
HMENU FinishMenu;

// 此代码模块中包含的函数的前向声明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	// TODO: 在此放置代码。

	// 初始化全局字符串
	LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadStringW(hInstance, IDC_MYTETRIS, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// 执行应用程序初始化: 
	if (!InitInstance(hInstance, nCmdShow))
	{
		return FALSE;
	}

	HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MYTETRIS));

	MSG msg;

	// 主消息循环: 
	while (1)
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))//消息检查线程消息队列
		{
			TranslateMessage(&msg);//将消息的虚拟键转化字符消息
			DispatchMessage(&msg);//将消息传送到指定窗口函数
			if (msg.message == WM_QUIT)//若消息是退出，则终止
			{
				break;
			}
		}
		else
		{
			if ((GAME_STATE & 2) != 0)
			{
				tCur = GetTickCount();//返回（retrieve）从操作系统启动所经过（elapsed）的毫秒数
				if (tCur - tPre>g_speed)//超过反应时间时
				{
					int flag = CheckValide(curPosX, curPosY + 1, bCurTeris);//判断下一行的情况，返回给flag
					if (flag == 1)//正常下降一行
					{
						curPosY++;//方块坐标y加1
						tPre = tCur;//重新计算反应时间
						HWND hWnd = GetActiveWindow();
						//这里两次调用实现了双缓冲的作用，改善了视觉效果
						InvalidateRect(hWnd, &rc_left, FALSE);//向指定的窗体更新区域添加一个矩形，然后窗口客户区域的这一部分将被重新绘制
						InvalidateRect(hWnd, &rc_right_top, FALSE);//系统不会像窗口发送WM_PAINT消息，来重绘
					}
					else if (flag == -2)//方块到底时
					{
						g_speed = t_speed;//重置速度
										  //fillBlock();//将方块填充给矩形
										  //checkComplite(); //查看能否消去这行
						setRandomT();//随机产生新的方块
						curPosX = (NUM_X - 4) >> 1;//重新设置方块的坐标x
						curPosY = 0;//重新设置方块的坐标y
						HWND hWnd = GetActiveWindow();//获得与调用线程的消息队列相关的活动窗口的窗口句柄
						InvalidateRect(hWnd, &rc_main, FALSE);//刷新矩形区域
					}
					else if (flag == -3)//放不下下一个方块（游戏输了）时
					{
						HWND hWnd = GetActiveWindow();//获得与调用线程的消息队列相关的活动窗口的窗口句柄
						if (MessageBox(hWnd, L"胜败乃兵家常事，菜鸡请重新来过", L":时光机", MB_YESNO) == IDYES)//消息提示，游戏失败
						{
							//init_game();//重新开始游戏
						}
						else
						{
							break;//退出了消息循环，结束程序
						}
					}
				}
			}
		}
	}

	return (int)msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYTETRIS));
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MYTETRIS);
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	hInst = hInstance; // 将实例句柄存储在全局变量中

	HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

	if (!hWnd)
	{
		return FALSE;
	}

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	//PAINTSTRUCT ps; /*包含应用程序用来绘制他拥有的窗口的客户区域的信息*/
	HDC hdc; /*标识设备环境句柄*/
			 //int nWinx, nWiny, nClientX, nClientY;
			 //int posX, posY;
			 //RECT rect; /*rect用来存储成对出现的参数，如矩形的坐标、高宽*/
	HMENU hSysmenu;/*菜单句柄*/
	switch (message)
	{
	case WM_CREATE:
		//GetWindowRect(hWnd, &rect);/*窗口的位置*/
		//nWinx = 530;/*窗口的宽度*/
		//nWiny = 680;/*窗口的高度*/
		//posX = GetSystemMetrics(SM_CXSCREEN);/*获取屏幕的宽度*/
		//posY = GetSystemMetrics(SM_CYSCREEN);/*获取屏幕的高度*/
		//posX = (posX - nWinx) >> 1;/*求出居中的x位置*/
		//posY = (posY - nWiny) >> 1;/*求出居中的y位置*/
		//GetClientRect(hWnd, &rect);/*获取客户区域的大小*/
		//nClientX = rect.right - rect.left;/*用户区域的宽度*/
		//nClientY = rect.bottom - rect.top;/*用户区域的高度*/

		//MoveWindow(hWnd, posX, posY, 530, 680, TRUE); /*改变指定窗口的位置和尺寸*/
		hSysmenu = GetMenu(hWnd);/*获取菜单句柄*/
		AppendMenu(hSysmenu, MF_SEPARATOR, 0, NULL); /*添加一条水平分割线*/
													 //此处的菜单句柄要全局向前初始化



		StartMenu = CreatePopupMenu();
		AppendMenu(hSysmenu, MF_POPUP, START_MENU, L"开始游戏");
		//AppendMenu(Diff, MF_STRING, ID_dif1, L"难度1");/*在新添加的菜单下创建菜单难度1*/
		//AppendMenu(Diff, MF_STRING, ID_dif2, L"难度2");/*在新添加的菜单下创建菜单难度2*/
		//AppendMenu(Diff, MF_STRING, ID_dif3, L"难度3");/*在新添加的菜单下创建菜单难度3*/

		FinishMenu = CreatePopupMenu();
		AppendMenu(hSysmenu, MF_POPUP, FINISH_MENU, L"结束游戏");
		//AppendMenu(LayOut, MF_STRING, ID_LAYOUT1, L"布局1");/*在新添加的LayOut菜单下创建菜单布局1*/
		//AppendMenu(LayOut, MF_STRING, ID_LAYOUT2, L"布局2");/*在新添加的LayOut菜单下创建菜单布局2*/
		SetMenu(hWnd, hSysmenu);
		SetMenu(hWnd, StartMenu);
		SetMenu(hWnd, FinishMenu);
		break;
	case WM_COMMAND:
	{
		int wmId = LOWORD(wParam);
		// 分析菜单选择: 
		switch (wmId)
		{
		case START_MENU:
			MessageBox(NULL, L"点击开始菜单", L"点击", MB_OK);
			//init_game();
			break;
		case FINISH_MENU:
			MessageBox(NULL, L"点击结束菜单", L"点击", MB_OK);
			break;
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
	}
	break;
	case WM_KEYDOWN://键盘消息
		hdc = GetDC(hWnd);
		InvalidateRect(hWnd, NULL, false);
		switch (wParam)
		{
		case VK_LEFT:
			MessageBox(NULL, L"敲击左键", L"敲击", MB_OK);
			break;
		case VK_RIGHT:
			MessageBox(NULL, L"敲击右键", L"敲击", MB_OK);
			break;
		case VK_UP:
			MessageBox(NULL, L"敲击上键", L"敲击", MB_OK);
			break;
		case VK_DOWN:
			MessageBox(NULL, L"敲击下键", L"敲击", MB_OK);
			break;
		default:
			break;
		}

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);
		hdc = BeginPaint(hWnd, &ps);
		// TODO: 在此添加任意绘图代码...
		DrawBackGround(hdc);//绘制背景
		EndPaint(hWnd, &ps);//停止绘制
		break;
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:

		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

int CheckValide(int startX, int startY, BOOL bTemp[4][4])//给定一个矩阵，查看是否合法
{
	int i, j;
	for (i = 3; i >= 0; i--)
	{
		for (j = 3; j >= 0; j--)
		{
			if (bTemp[i][j])
			{
				if (j + startX<0 || j + startX >= NUM_X)
				{
					return -1;
				}
				if (i + startY >= NUM_Y)
				{
					return -2;
				}
				if (g_hasBlocked[i + startY][j + startX])
				{
					//outPutBoxInt(j+startY);
					if (curPosY == 0)
					{
						return -3;
					}
					return -2;
				}
			}
		}
	}
	//MessageBox(NULL,L"这里",L"as",MB_OK);
	//outPutBoxInt(curPosY);
	return 1;
}
void RotateTeris(BOOL bTeris[4][4])
{
	BOOL bNewTeris[4][4];
	int x, y;
	for (x = 0; x<4; x++)
	{
		for (y = 0; y<4; y++)
		{//旋转角度
			bNewTeris[x][y] = bTeris[3 - y][x];
			//逆时针：
			//bNewTeris[x][y] = bTeris[y][3-x];
		}
	}
	if (CheckValide(curPosX, curPosY, bNewTeris) == 1)
	{
		memcpy(bTeris, bNewTeris, sizeof(bNewTeris));//成功则将变换后的形状保存
	}
}
/*TEST(FooTest, HandleNoneZeroInput)    //gtest测试
{
BOOL bTemp[4][4];
EXPECT_EQ(-1, CheckValide(4, 10,bTemp));
EXPECT_EQ(-2, CheckValide(4, 10,bTemp));
EXPECT_EQ(-3, CheckValide(4, 10,bTemp));
EXPECT_EQ(0, CheckValide(4, 10,bTemp));
}*/

void setRandomT()
{
	int rd_start = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	int rd_next = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	//outPutBoxInt(rd_start);
	//outPutBoxInt(rd_next);
	//outPutBoxInt(rd_start);
	if (GAME_STATE == 0)
	{
		GAME_STATE = GAME_STATE | 0x0001;
		//outPutBoxInt(GAME_STATE);
		memcpy(bCurTeris, state_teris[rd_start], sizeof(state_teris[rd_start]));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}
	else
	{
		memcpy(bCurTeris, bNextCurTeris, sizeof(bNextCurTeris));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}

}

int RandomInt(int _min, int _max)
{
	srand((rd_seed++) % 65532 + GetTickCount());
	return _min + rand() % (_max - _min);
}

void drawBlocked(HDC mdc)//绘制当前已经存在砖块的区域
{
	int i, j;
	//在应用程序调用CreateSolidBrush创建刷子以后，可以通过调用SelectObject函数把该刷子选入设备环境
	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(255, 255, 0));//画刷句柄，黄色

	SelectObject(mdc, hBrush);//函数选择一对象到指定的设备上下文环境中，新对象替换先前的相同类型的对象

	for (i = 0; i<NUM_Y; i++)
	{
		for (j = 0; j<NUM_X; j++)
		{
			if (g_hasBlocked[i][j])
			{
				Rectangle(mdc, BORDER_X + j * BLOCK_SIZE, BORDER_Y + i * BLOCK_SIZE,
					BORDER_X + (j + 1)*BLOCK_SIZE, BORDER_Y + (i + 1)*BLOCK_SIZE
				);//绘画矩形的函数，并用当前颜色的画刷进行颜色的填充
			}
		}
	}
	DeleteObject(hBrush);//该函数删除一个逻辑笔、画笔、字体、 位图、区域或者调色板，释放所有与该对象有关的系统资源
}
void drawNext(HDC hdc)//绘制下一个将要掉落的方块
{
	int i, j;
	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(0, 188, 0));
	SelectObject(hdc, hBrush);
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<4; j++)
		{
			if (bNextCurTeris[i][j])
			{
				Rectangle(hdc, rc_right_top.left + BLOCK_SIZE * (j + 1), rc_right_top.top + BLOCK_SIZE * (i + 1), rc_right_top.left + BLOCK_SIZE * (j + 2), rc_right_top.top + BLOCK_SIZE * (i + 2));
			}
		}
	}
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 0));
	RECT rect;
	rect.left = rc_right_top.left + 40;
	rect.top = rc_right_top.bottom - 150;
	rect.right = rc_right_top.right;
	rect.bottom = rc_right_top.bottom;
	DrawTextW(hdc, TEXT("下一个"), _tcslen(TEXT("下一个")), &rect, 0);
	DeleteObject(hFont);
	DeleteObject(hBrush);
}

void drawScore(HDC hdc)
{
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 0));
	RECT rect;
	rect.left = rc_right_bottom.left;
	rect.top = rc_right_bottom.top;
	rect.right = rc_right_bottom.right;
	rect.bottom = rc_right_bottom.bottom;
	TCHAR szBuf[30];
	LPCTSTR cstr = TEXT("当前难度：%d");
	wsprintf(szBuf, cstr, GAME_DIFF);
	DrawTextW(hdc, szBuf, _tcslen(szBuf), &rect, DT_CENTER | DT_VCENTER);

	RECT rect2;
	rect2.left = rc_right_bottom.left;
	rect2.top = rc_right_bottom.bottom / 2 + 100;
	rect2.right = rc_right_bottom.right;
	rect2.bottom = rc_right_bottom.bottom;
	TCHAR szBuf2[30];
	LPCTSTR cstr2 = TEXT("得分：%d");
	wsprintf(szBuf2, cstr2, GAME_SCORE);
	//outPutBoxInt(sizeof(szBuf));
	DrawTextW(hdc, szBuf2, _tcslen(szBuf2), &rect2, DT_CENTER | DT_VCENTER);

	DeleteObject(hFont);
}

void drawCompleteParticle(int line)
{
	HWND hWnd = GetActiveWindow();
	HDC hdc = GetDC(hWnd);
	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	HPEN hPen = (HPEN)CreatePen(PS_DOT, 1, RGB(255, 255, 0));
	SelectObject(hdc, hBrush);
	SelectObject(hdc, hPen);
	Rectangle(hdc, BORDER_X,
		BORDER_Y + line * BLOCK_SIZE,
		BORDER_X + NUM_X * BLOCK_SIZE,
		BORDER_Y + BLOCK_SIZE * (1 + line));
	DeleteObject(hBrush);
	DeleteObject(hPen);
}
VOID DrawTeris(HDC mdc)
{

	int i, j;
	HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);//画笔句柄
	HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
	SelectObject(mdc, hPen);//electObject是计算机编程语言函数，该函数选择一对象到指定的设备上下文环境中，新对象替换先前的相同类型的对象
	SelectObject(mdc, hBrush);
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<4; j++)
		{
			if (bCurTeris[i][j])
			{
				Rectangle(mdc, (j + curPosX)*BLOCK_SIZE + BORDER_X, (i + curPosY)*BLOCK_SIZE + BORDER_Y, (j + 1 + curPosX)*BLOCK_SIZE + BORDER_X, (i + 1 + curPosY)*BLOCK_SIZE + BORDER_Y);
			}
		}
	}
	drawBlocked(mdc);
	DeleteObject(hPen);
	DeleteObject(hBrush);
}

VOID DrawBackGround(HDC hdc)//绘制背景
{

	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	//适用于支持光栅操作的设备，应用程序可以通过调用GetDeviceCaps函数来确定一个设备是否支持这些操作
	HDC mdc = CreateCompatibleDC(hdc);
	HBITMAP hBitmap = CreateCompatibleBitmap(hdc, SCREEN_X, SCREEN_Y);

	SelectObject(mdc, hBrush);
	SelectObject(mdc, hBitmap);

	HBRUSH hBrush2 = (HBRUSH)GetStockObject(WHITE_BRUSH);
	FillRect(mdc, &rc_main, hBrush2);
	Rectangle(mdc, rc_left.left + BORDER_X, rc_left.top + BORDER_Y, rc_left.right, rc_left.bottom);
	Rectangle(mdc, rc_right.left + BORDER_X, rc_right.top + BORDER_Y, rc_right.right, rc_right.bottom);
	DrawTeris(mdc);
	drawNext(mdc);
	drawScore(mdc);
	::BitBlt(hdc, 0, 0, SCREEN_X, SCREEN_Y, mdc, 0, 0, SRCCOPY);
	DeleteObject(hBrush);
	DeleteDC(mdc);
	DeleteObject(hBitmap);
	DeleteObject(hBrush2);


	//  int x,y;
	//  HPEN hPen = (HPEN)GetStockObject(NULL_PEN);
	//  HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	//  SelectObject(hdc,hPen);
	//  SelectObject(hdc,hBrush);
	//  for (x = 0;x<NUM_X;x++)
	//  {
	//      for(y=0;y<NUM_Y;y++)
	//      {
	//          Rectangle(hdc,BORDER_X+x*BLOCK_SIZE,BORDER_Y+y*BLOCK_SIZE,
	//              BORDER_X+(x+1)*BLOCK_SIZE,
	//              BORDER_Y+(y+1)*BLOCK_SIZE);
	//      }
	//  }
}

/*void init_game()
{
GAME_SCORE = 0;
setRandomT();//随机生成一个方块用作下一次掉落
curPosX = (NUM_X - 4) >> 1;//设置初始坐标
curPosY = 0;
//memset，是计算机语言中的函数。起功能是将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值，最后行清零
memset(g_hasBlocked, 0, sizeof(g_hasBlocked));
rc_left.left = 0;
rc_left.right = SCREEN_LEFT_X;
rc_left.top = 0;
rc_left.bottom = SCREEN_Y;

rc_right.left = rc_left.right + BORDER_X;
rc_right.right = 180 + rc_right.left;
rc_right.top = 0;
rc_right.bottom = SCREEN_Y;

rc_main.left = 0;
rc_main.right = SCREEN_X;
rc_main.top = 0;
rc_main.bottom = SCREEN_Y;

rc_right_top.left = rc_right.left;
rc_right_top.top = rc_right.top;
rc_right_top.right = rc_right.right;
rc_right_top.bottom = (rc_right.bottom) / 2;

rc_right_bottom.left = rc_right.left;
rc_right_bottom.top = rc_right_top.bottom + BORDER_Y;
rc_right_bottom.right = rc_right.right;
rc_right_bottom.bottom = rc_right.bottom;

g_speed = t_speed = 1000 - GAME_DIFF * 280;
}*/